#include "RecordDatabase.h"

#include <QMessageBox>

/*--------------------------------------------------------
 *
 * Record
 *
 *--------------------------------------------------------*/

Record::Record()
{
    temperature = weight = bloodGlucose = bloodPressureSystolic
        = bloodPressureDiastolic = respiratoryRate = pulseRate = recordId = -1;

    commentPatient = "";
    commentDoctor = "";
}

/*--------------------------------------------------------
 *
 * RecordDatabase
 *
 *--------------------------------------------------------*/

RecordDatabase::RecordDatabase()
{
    model = nullptr;

    initDatabase();
}

RecordDatabase::~RecordDatabase()
{
    if (model != nullptr)
        delete model;
}

void RecordDatabase::initDatabase()
{
    DatabaseLock lock(this);
    QSqlQuery query;

    // Check if user_auth table already exists
    query.exec("SELECT name FROM sqlite_master WHERE type='table' AND name='records'");

    // If query returns a value, the table already exists and initialization can be skipped
    if (query.next())
        return;

    // Create table to hold user health records
    query.exec("CREATE TABLE IF NOT EXISTS records ("
               "recordId INTEGER PRIMARY KEY AUTOINCREMENT, "
               "userId INTEGER, "
               "observationTime TEXT, "
               "temperature REAL, "
               "weight REAL, "
               "bloodGlucose REAL, "
               "bloodPressureSystolic INTEGER, "
               "bloodPressureDiastolic INTEGER, "
               "respiratoryRate INTEGER, "
               "pulseRate INTEGER, "
               "commentPatient TEXT, "
               "commentDoctor TEXT)");
}

void RecordDatabase::addRecord(const Record * record, int userId)
{
    DatabaseLock lock(this);
    QSqlQuery query;

    query.prepare("INSERT INTO records (userId, observationTime, temperature, "
                  "weight, bloodGlucose, bloodPressureSystolic, "
                  "bloodPressureDiastolic, respiratoryRate, pulseRate,"
                  "commentPatient, commentDoctor) VALUES (?,?,?,?,?,?,?,?,?,?,?)");
    query.addBindValue(userId);
    query.addBindValue(QVariant(record->observationDate.toString(
                                                    "yyyy-MM-dd HH:mm:ss")));
    bindNumCheck(query, record->temperature);
    bindNumCheck(query, record->weight);
    bindNumCheck(query, record->bloodGlucose);
    bindNumCheck(query, record->bloodPressureSystolic);
    bindNumCheck(query, record->bloodPressureDiastolic);
    bindNumCheck(query, record->respiratoryRate);
    bindNumCheck(query, record->pulseRate);
    query.addBindValue(QVariant(record->commentPatient));
    query.addBindValue(QVariant(record->commentDoctor));
    query.exec();
}

void RecordDatabase::addComment(const QString comment, int recordId)
{
    DatabaseLock lock(this);
    QSqlQuery query;

    query.prepare("UPDATE records SET commentDoctor=:comment "
                  "WHERE recordId=:recordId");
    query.addBindValue(QVariant(comment));
    query.addBindValue(QVariant(recordId));
    query.exec();
}

void RecordDatabase::loadRecords(int userId)
{
    records.clear();

    DatabaseLock lock(this);
    QSqlQuery query;

    query.prepare("SELECT * FROM records "
                  "WHERE userId=:userId "
                  "ORDER BY observationTime");
    query.bindValue(":userId", userId);
    query.exec();

    while (query.next())
    {
        Record rec = parseQuery(query);
        records.push_back(rec);
    }

    if (model != nullptr)
        delete model;

    model = new RecordDatabaseModel();
    model->setSource(&records);
}

RecordDatabaseModel * RecordDatabase::getModel()
{
    return model;
}

QList<Record> * RecordDatabase::getRecords()
{
    return &records;
}

Record RecordDatabase::getRecordById(int recordId)
{
    DatabaseLock lock(this);
    QSqlQuery query;

    query.prepare("SELECT * FROM records WHERE recordId=:recordId");
    query.addBindValue(recordId);
    query.exec();

    if (!query.next())
        return Record();

    Record rec = parseQuery(query);
    return rec;
}

int RecordDatabase::getRecordCount()
{
    return recordCount;
}

Record RecordDatabase::parseQuery(const QSqlQuery &query) const
{
    Record rec;

    rec.recordId = query.value(0).toInt();
    rec.observationDate = QDateTime::fromString(query.value(2).toString(),
                                                "yyyy-MM-dd HH:mm:ss");
    rec.temperature = query.value(3).toFloat();
    rec.weight = query.value(4).toFloat();
    rec.bloodGlucose = query.value(5).toFloat();
    rec.bloodPressureSystolic = query.value(6).toInt();
    rec.bloodPressureDiastolic = query.value(7).toInt();
    rec.respiratoryRate = query.value(8).toInt();
    rec.pulseRate = query.value(9).toInt();
    rec.commentPatient = query.value(10).toString();
    rec.commentDoctor = query.value(11).toString();

    return rec;
}

template<typename T>
void RecordDatabase::bindNumCheck(QSqlQuery &query, T value)
{
    QVariant bindValue;

    if (value > 0)
        bindValue = QVariant(value);

    query.addBindValue(bindValue);
}

/*--------------------------------------------------------
 *
 * RecordDatabaseModel
 *
 *--------------------------------------------------------*/

RecordDatabaseModel::RecordDatabaseModel()
{
    this->records = nullptr;

    rowNum = 0;
    colNum = 10;
}

void RecordDatabaseModel::setSource(QList<Record> *records)
{
    this->records = records;

    rowNum = records->size();
}

int RecordDatabaseModel::rowCount(const QModelIndex &) const
{
    return rowNum;
}

int RecordDatabaseModel::columnCount(const QModelIndex &) const
{
    return colNum;
}

void RecordDatabaseModel::sort(int column, Qt::SortOrder order)
{
    if (column != 0)
        return;

    qSort(records->begin(), records->end(),
          [&](const Record &r1, const Record &r2) -> bool {
                if (order == Qt::AscendingOrder)
                    return r1.observationDate < r2.observationDate;
                else
                    return r1.observationDate > r2.observationDate;
    });

    emit dataChanged(createIndex(0, 0), createIndex(records->size(), colNum));
}

QVariant RecordDatabaseModel::data(const QModelIndex &index, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant::Invalid;

    const Record *record = &( records->at(index.row()) );

    switch(index.column())
    {
        case 0: return QVariant(record->observationDate);
        case 1: return QVariant(record->temperature);
        case 2: return QVariant(record->weight);
        case 3: return QVariant(record->bloodGlucose);
        case 4: return QVariant(record->bloodPressureSystolic);
        case 5: return QVariant(record->bloodPressureDiastolic);
        case 6: return QVariant(record->respiratoryRate);
        case 7: return QVariant(record->pulseRate);
        case 8: return QVariant(record->commentPatient);
        case 9: return QVariant(record->commentDoctor);
    }

    return QVariant::Invalid;
}

QVariant RecordDatabaseModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    QString header = "";

    if (role != Qt::DisplayRole && orientation == Qt::Horizontal)
        return QVariant::Invalid;

    switch(section)
    {
        case 0: header = "Date"; break;
        case 1: header = "Temperature"; break;
        case 2: header = "Weight"; break;
        case 3: header = "Glucose"; break;
        case 4: header = "Blood Pressure Sys"; break;
        case 5: header = "Blood Pressure Dia"; break;
        case 6: header = "Respiratory Rate"; break;
        case 7: header = "Pulse Rate"; break;
        case 8: header = "Comment (Patient)"; break;
        case 9: header = "Comment (Doctor)";
    }

    return QVariant(header);
}
